The UpdatePanel
provides a way to update only a portion of the page. That's pretty
amazing. However, AJAX's compelling features have a very broad reach.
One of the most useful features is the extender control architecture.
Extender controls target existing controls to extend functionality in the target. Whereas controls such as the ScriptManager and the Timer
do a lot in terms of injecting script code into the page as the page is
rendered, the extender controls often manage the markup (HTML) in the
resulting page.
The following subsections discuss the ASP.NET AJAX extender controls. The first one is the AutoComplete extender.
1. The AutoComplete Extender
The AutoComplete extender attaches to a standard ASP.NET TextBox. As the end user types text in the TextBox, the AutoComplete
extender calls a Web service to look up candidate entries based on the
results of the Web service call.
Using the AutoComplete extender
Add a new page to AJAXORama. Because this page will host the AutoComplete extender, name it UseAutocompleteExtender.
Add an instance of the ScriptManager control to the page you just added.
The class derives from System.Data.Table
and holds a collection of famous quotes and their originators.
Add
a method to retrieve the quotes based on the last name. The method
should accept the last name of the originator as a string parameter. The
System.Data.DataView
class you use for retrieving a specific quote is useful for performing
queries on a table in memory. The method should return the quotes as a
list of strings. There might be none, one, or many, depending on the
selected quote author. You use this function shortly.
using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Collections.Generic;
/// <summary>
/// Summary description for QuotesCollection
/// </summary>
public class QuotesCollection : DataTable
{
public QuotesCollection()
{ }
public void Synthesize()
{
this.TableName = "Quotations";
DataRow dr;
Columns.Add(new DataColumn("Quote", typeof(string)));
Columns.Add(new DataColumn("OriginatorLastName", typeof(string)));
Columns.Add(new DataColumn(@"OriginatorFirstName",
typeof(string)));
dr = this.NewRow();
dr[0] = "Imagination is more important than knowledge.";
dr[1] = "Einstein";
dr[2] = "Albert";
Rows.Add(dr);
// Other quotes added here...
}
public string[]
GetQuotesByLastName(string strLastName)
{
List<string> list = new List<string>();
DataView dvQuotes = new DataView(this);
string strFilter = String.Format("OriginatorLastName = '{0}'", strLastName);
dvQuotes.RowFilter = strFilter;
foreach (DataRowView drv in dvQuotes)
{
string strQuote =
drv["Quote"].ToString();
list.Add(strQuote);
}
return list.ToArray();
}
}
Add a class named QuotesManager to the project. The class manages caching. The caching example from which this code is borrowed stores and retrieves the QuotesCollection during the Page_Load event. Because the QuotesCollection will be used within a Web service, the caching has to happen elsewhere. To do this, add a public static method named GetQuotesFromCache to retrieve the QuotesCollection from the cache:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
/// <summary>
/// Summary description for QuotesManager
/// </summary>
public class QuotesManager
{
public QuotesManager()
{
}
public static QuotesCollection GetQuotesFromCache()
{
QuotesCollection quotes;
quotes =
(QuotesCollection)HttpContext.Current.Cache["quotes"];
if (quotes == null)
{
quotes = new QuotesCollection();
quotes.Synthesize();
}
return quotes;
}
}
Add an XML Web Service to your application. Right-click the project and add an ASMX file to your application. Name the service QuoteService. You can remove the WebService and WebServiceBinding attributes, but be sure to adorn the XML Web Service class with the [System.Web.Script.Services.ScriptService] attribute by uncommenting it (Visual Studio put it in for you). That way, it is available to the AutoComplete extender later on. The AutoCompleteExtender uses the XML Web Service to populate its drop-down list box.
Add
a method to get the last names of the quote originators—that's the
method that populates the drop-down box. The method should take a string
representing the text already typed in as the first parameter, an
integer representing the maximum number of strings to return. Grab the QuotesCollection from the cache using the QuoteManager's static method GetQuotesFromCache. Use the QuotesCollection to get the rows from the QuotesCollection.
Finally, iterate through the rows and add the originator's last name to
the list of strings to be returned if it starts with the prefix passed
in as the parameter. The Common Language Runtime (CLR) String type includes a method named StartsWith that's useful to figure out whether a string starts with a certain prefix. Note that you also have to add using statements for generic collections and data as shown:
using System;
using System.Linq;
using System.Web;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Services;
using System.Data;
[System.Web.Script.Services.ScriptService]
public class QuoteService : System.Web.Services.WebService
{
[WebMethod]
public string[]
GetQuoteOriginatorLastNames(string prefixText,
int count)
{
List<string> list = new List<string>();
QuotesCollection quotes =
QuotesManager.GetQuotesFromCache();
prefixText = prefixText.ToLower();
foreach (DataRow dr in quotes.Rows)
{
string strName =
dr["OriginatorLastName"].ToString();
if (strName.ToLower().StartsWith(prefixText))
{
if (!list.Contains(strName))
{
list.Add(strName);
}
}
}
return list.GetRange(0,
System.Math.Min(count, list.Count)).ToArray();
}
}
Now drop a TextBox on the UseAutocompleteExtender page to hold the originator's last name to be looked up. Give the TextBox an ID of TextBoxOriginatorLastName.
Drag an AutoCompleteExtender from the AJAX Toolbox and add it to the page. Set its ID to be AutoCompleteExtenderForOriginatorLastName. Point the AutoComplete TargetControlID to the TextBox holding the originator's last name, TextBoxOriginatorLastName. Make the MinimumPrefix length 1, the ServiceMethod GetQuoteOriginatorLastNames, and the ServicePath quoteservice.asmx. This wires up the AutoComplete extender so that it takes text from the TextBoxOriginatorLastName TextBox and uses it to feed the XML Web Service GetQuoteOriginatorLastNames method.
<cc1:AutoCompleteExtender
ID="AutoCompleteExtenderForOriginatorLastName"
TargetControlID="TextBoxOriginatorLastName"
MinimumPrefixLength="1"
ServiceMethod="GetQuoteOriginatorLastNames"
ServicePath="quoteservice.asmx"
runat="server">
</cc1:AutoCompleteExtender>
Add a TextBox to the page to hold the quotes. Name the TextBox TextBoxQuotes.
Update the Page_Load method. It should look up the quotes based on the name in the text box by retrieving the QuotesCollection and calling the QuotesCollection GetQuotesByLastName method:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Text;
public partial class UseAutocompleteExtender :
System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
QuotesCollection quotes =
QuotesManager.GetQuotesFromCache();
string[] quotesArray =
quotes.GetQuotesByLastName(TextBoxOriginatorLastName.Text);
if (quotesArray != null && quotesArray.Length > 0)
{
StringBuilder str = new StringBuilder();
foreach (string s in quotesArray)
{
str.AppendFormat("{0}\r\n", s);
}
this.TextBoxQuotes.Text = str.ToString();
}
else
{
this.TextBoxQuotes.Text = "No quotes match your request.";
}
}
}
To make the page updates more efficient, drop an UpdatePanel onto the page. Put the TextBox for holding the quotes in the UpdatePanel. This causes only the TextBox showing the quotes to be updated instead of performing a whole-page refresh. Add a button following the originator's last name TextBox with the ID ButtonFindQuotes.
Add two asynchPostBack triggers to the UpdatePanel. The first trigger should connect the TextBoxOriginatorLastName TextBox to the TextChanged event. The second trigger should connect the ButtonFindQuotes button to the button's Click event.
The following graphic shows the layout of the page using the AutoCompleteExtender in action:
Run the page. As you type originator names into the TextBox, you should see a drop-down list appear containing candidate names based on the QuotesCollection's contents.
The AutoComplete extender is an excellent example of the capabilities that ASP.NET AJAX
support includes. Internet Explorer has had an autocomplete feature
built in for quite a while. Internet Explorer remembers often-used names
of HTML input text tags and recent values that have been used for them.
For example, when you go online to buy an airline ticket and then go
back to buy another one later, watch what happens as you type in the Web
address. The Internet
Explorer autocomplete feature makes available a drop-down list below
the address bar that shows the last few addresses you've typed in that
begin with the same text you began typing in the text box.
The ASP.NET AutoComplete
extender works very much like this. However, the major difference is
that the end user sees input candidates generated by the Web site rather
than simply a history of recent entries. Of course, the Web site could
mimic this functionality by tracking a user's profile identity and store
a history of what a particular user has typed in to a specific input
field on a page. The actual process of generating autocomplete
candidates is completely up to the Web server, giving a whole new level
of power and flexibility in programming user-friendly Web sites.